{UText2}    {Paragraph Classes}

{changed 05/11/84 1135  Added TParagraph.Clone}
{changed 04/25/84 1250  Changed FilterAndDo calls back to filtering TParaImage for Compugraphic}
{changed 04/18/84 1652  Use TTextImage.firstLinePixel in DrawParaImage}
{changed 04/17/84 1806  Put call to ReplTString outside of IF in ReplPara;
                        Put more parameter checks in ReplTString}
{changed 04/16/84 1135  Added styleSheet field to TParaFormat; use it in ChangeRefCountBy}
{changed 04/16/84 1033  Put PicTextBegin, End in TParagraph.DrawLine;
                        Put PicGrpBegin, End in TParaImage.RedrawLines;
                        Removed picture comments from TParaImage.DrawLine}
{changed 04/13/84 1739  Set paraformat.refcount = 0 in TParaFormat.Clone}
{changed 04/13/84 1537  Changed calls to FilterAndDo to pass TEditPara rather than TParaImage}
{changed 04/12/84 2344  Modified UpdateRuns to use new parameter list}
{changed 04/11/84 1527  Call UpdateRuns after deleting characters in TParagraph.ReplPString and ReplTString}
{changed 04/11/84 1454  Debug statement in Qualifies to check bug involving special characters}
{changed 04/10/84 1400  Changed TEditPara.images field back to a TList and adusted references to it}
{changed 04/10/84 1158  Put calls to TParaFormat.ChangeRefCountBy in TEditPara.CREATE, Free}

{$IFC fRngABC}
{$R+}
{$ELSEC}
{$R-}
{$ENDC}

{$IFC fSymABC}
{$D+}
{$ELSEC}
{$D-}
{$ENDC}

TYPE
    TScanState = (cBeforeRange, cInRange, cAfterRange);
    TFakeTStyle = PACKED ARRAY[1..SIZEOF(TTypeStyle)] OF CHAR;
    {$IFC LibraryVersion <= 20}
    Style = TSeteface;
    FontInfo = TFinfo;
    {$ENDC}


{$S SgTxtHot}

VAR nextHighTransit:    THighTransit;
    nextTransitTime:    LONGINT;
    uvFont:	        ARRAY [1..14] OF TFontRecord;


METHODS OF TParaFormat;

{$S SgTxtIni}
    FUNCTION TParaFormat.CREATE(object: TObject; heap: THeap; itsStyleSheet: TStyleSheet): TParaFormat;
    VAR tabArray:   TArray;
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TParaFormat(object);
        tabArray := TArray.CREATE(NIL, heap, 0, SIZEOF(TTxtTabDescriptor));
        WITH SELF DO
            BEGIN
            {$H-}
            MakeTypeStyle(famModern, size12Pitch, [], dfltTstyle);
            {$H+}
            wordWrap := TRUE;
            quad := aLeft;
            firstIndent := 0;
            leftIndent := 0;
            rightIndent := 0;
            spaceAbovePara := 0;
            spaceBelowPara := 0;
            lineSpacing := 0;
            tabs := tabArray;
            permanent := FALSE;
            refCount := 0;
            styleSheet := itsStyleSheet;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
    PROCEDURE TParaFormat.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        Free(SELF.tabs);
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TParaFormat.Clone(heap: THeap): TObject;
    VAR tabs:	    TArray;
        paraFormat: TParaFormat;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        tabs := TArray(SELF.tabs.Clone(heap));
        paraFormat := TParaFormat(SUPERSELF.Clone(heap));
        paraFormat.tabs := tabs;
        paraFormat.refCount := 0;
        Clone := paraFormat;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
{$IFC fParaTrace}
    PROCEDURE TParaFormat.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('dfltTstyle: RECORD onFaces: HexByte; filler: HexByte; fontFamily: Byte; fontSize: Byte END');
        Field('wordWrap: BOOLEAN');
        Field('quad: HexByte');
        Field('firstIndent: INTEGER');
        Field('leftIndent: INTEGER');
        Field('rightIndent: INTEGER');
        Field('spaceAbovePara: INTEGER');
        Field('spaceBelowPara: INTEGER');
        Field('lineSpacing: INTEGER');
        Field('tabs: TArray');
        Field('refCount: INTEGER');
        Field('permanent: BOOLEAN');
        Field('styleSheet: BOOLEAN');
        Field('');
    END;
{$ENDC}


{$S SgTxtCld}
    PROCEDURE TParaFormat.ChangeRefCountBy(delta: INTEGER);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.refCount := SELF.refCount + delta;
        IF (SELF.refCount <= 0) AND NOT SELF.permanent THEN
            BEGIN
            IF SELF.styleSheet <> NIL THEN
                SELF.styleSheet.formats.DelObject(SELF, TRUE)
            ELSE
                SELF.Free;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaFormat.SetTypeStyle(tStyle: TTypeStyle);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SetQDTypeStyle(tStyle);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
BEGIN
    UnitAuthor('Apple');
END;


METHODS OF TParagraph;

{$S SgTxtIni}
    FUNCTION TParagraph.CREATE(object: TObject; heap: THeap;
                                        initialSize: INTEGER; initialTypeStyle: TTypeStyle): TParagraph;
    VAR ts:	        TArray;
        styleChange:    TStyleChange;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF object = NIL THEN
            object := NewDynObject(heap, THISCLASS, initialSize);
        SELF := TParagraph(TString.CREATE(object, heap, initialSize));

        ts := TArray.CREATE(NIL, heap, 0, SIZEOF(TStyleChange));

        styleChange.lp := MAXINT; { -sentinel- }
        styleChange.newStyle := initialTypeStyle;
        ts.InsAt(1, @styleChange);
        styleChange.lp := -1;
        ts.InsAt(1, @styleChange);

        SELF.typeStyles := ts;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
    PROCEDURE TParagraph.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        Free(SELF.typeStyles);
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TParagraph.Clone(heap: THeap): TObject;
    VAR paragraph:  TParagraph;
        styles:	    TArray;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        styles := TArray(SELF.typeStyles.Clone(heap));
        paragraph := TParagraph(SUPERSELF.Clone(heap));
        paragraph.typeStyles := styles;
        Clone := paragraph;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
{$IFC fParaTrace}
    PROCEDURE TParagraph.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field(CONCAT('typeStyles: TArray OF RECORD lp: INTEGER; onFaces: HexByte; ',
                                            'filler: HexByte; fontFamily: Byte; fontSize: Byte END'));
        Field('');
    END;
{$ENDC}


{$S TK2Start}
  {BuildExtentLRect takes an LPoint that indicates the baseline of the paragraph.  It returns
   in extentLRect the bounding rectangle whose height is based on the tallest font in the
   paragraph and width is the width of the characters in the paragraph.}
    PROCEDURE TParagraph.BuildExtentLRect(baseLPt: LPoint; VAR extentLRect: LRect);
    VAR styleChange:    TStyleChange;
        fInfo:	        FontInfo;
        i:	        INTEGER;
        tallestFont:    FontInfo;
        width:	        INTEGER;
        oldTallest:     INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        oldTallest := 0;
        FOR i := 1 to SELF.typeStyles.size - 1 DO
            BEGIN
            SELF.typeStyles.GetAt(i, @styleChange);
            SELF.SetTypeStyle(styleChange.newStyle);
            GetFontInfo(fInfo);
            WITH fInfo DO
                IF oldTallest < ascent + descent + leading THEN
                    BEGIN
                    oldTallest := ascent + descent + leading;
                    tallestFont := fInfo;
                    END;
            END;
        width := SELF.Width(1, SELF.size);
        WITH extentLRect, baseLPt, tallestFont DO
            BEGIN
            top := v - ascent;
            bottom := v + descent + leading;
            left := h;
            right := h + width;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParagraph.ChangeStyle(startLP, endLP: INTEGER; PROCEDURE Change(VAR typeStyle: TTypeStyle);
                                                        VAR styleOfStartLP: TTypeStyle);
    VAR firstChange:    TStyleChange;
        tempChange:     TStyleChange;
        prevChange:     TStyleChange;
        styles:	        TArray;
        styleIndex:     INTEGER;
        newStyle:       TTypeStyle;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            WriteLn('=== Entering TParagraph.ChangeStyle: startLP=', startLP:1, '  endLP=', endLP:1);
            END;
        {$ENDC}
        styles := SELF.typeStyles;

        styleIndex := 1;
        REPEAT
            styleIndex := styleIndex + 1;
            styles.GetAt(styleIndex, @tempChange);
        UNTIL tempChange.lp >= startLP;

        {If the change is on a run boundary, just remember the changed style at the beginning so
         we can set styleOfStartLP later}
        IF tempChange.lp = startLP THEN
            BEGIN
            firstChange := tempChange;
            Change(firstChange.newStyle);
            END
        ELSE
            BEGIN
            {Insert new run descriptor for beginning of changed characters}
            styles.GetAt(styleIndex - 1, @firstChange);
            prevChange := firstChange;
            firstChange.lp := startLP;
            Change(firstChange.newStyle);
            styles.InsAt(styleIndex, @firstChange);
            styleIndex := styleIndex + 1;
            END;

        {Change existing runs}
        WHILE (tempChange.lp < endLP) DO
            BEGIN
            prevChange := tempChange;
            Change(tempChange.newStyle);
            styles.PutAt(styleIndex, @tempChange);
            styleIndex := styleIndex + 1;
            styles.GetAt(styleIndex, @tempChange);
            END;

        {Don't restore old run info if new run info goes to end of para or ends on old run boundary}
        IF endLP < SELF.size THEN
            IF tempChange.lp <> endLP THEN
                BEGIN
                prevChange.lp := endLP;
                styles.InsAt(styleIndex, @prevChange);
                END;

        SELF.CleanRuns;

        {return typestyle of beginning of run}
        styleOfStartLP := firstChange.newStyle;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParagraph.ChgFace(startLP, endLP: INTEGER;
                                        newOnFaces: {$IFC LibraryVersion <= 20}TSeteface{$ELSEC}Style{$ENDC};
                                                        VAR styleOfStartLP: TTypeStyle);
        PROCEDURE ChangeFace(VAR typeStyle: TTypeStyle);
        BEGIN
            IF newOnFaces = [] THEN
                typeStyle.onFaces := []
            ELSE
                typeStyle.onFaces := typeStyle.onFaces + newOnFaces;
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.ChangeStyle(startLP, endLP, ChangeFace, styleOfStartLP);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParagraph.ChgFontFamily(startLP, endLP: INTEGER; newFontFamily: Byte;
                                                        VAR styleOfStartLP: TTypeStyle);
        PROCEDURE ChangeFamily(VAR typeStyle: TTypeStyle);
        BEGIN
            typeStyle.font.fontFamily := newFontFamily;
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.ChangeStyle(startLP, endLP, ChangeFamily, styleOfStartLP);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParagraph.ChgFontSize(startLP, endLP: INTEGER; newFontSize: Byte;
                                                        VAR styleOfStartLP: TTypeStyle);
        PROCEDURE ChangeSize(VAR typeStyle: TTypeStyle);
        BEGIN
            typeStyle.font.fontSize := newFontSize;
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.ChangeStyle(startLP, endLP, ChangeSize, styleOfStartLP);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    {Deletes redundant run information}
    PROCEDURE TParagraph.CleanRuns;
    VAR styleChange:    TStyleChange;
        prevChange:     TStyleChange;
        styles:	        TArray;
        styleIndex:     INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        styles := SELF.typeStyles;
        styles.GetAt(1, @prevChange);
        styleIndex := 2;
        {Iterate through the style changes and delete any that have either the same lp as the previous
         change or the same font and faces info}
        WHILE styleIndex < styles.Size DO
            BEGIN
            styles.GetAt(styleIndex, @styleChange);
            IF (styleChange.lp = prevChange.lp) OR
                        ((styleChange.newStyle.onFaces = prevChange.newStyle.onFaces) AND
                        (styleChange.newStyle.font.fontNum = prevChange.newStyle.font.fontNum)) THEN
                styles.DelAt(styleIndex)
            ELSE
                styleIndex := styleIndex + 1;
            prevChange := styleChange;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtHot}
    PROCEDURE TParagraph.Draw(i: LONGINT; howMany: INTEGER);
    VAR dumInt:	    INTEGER;
        dumIndex:   INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        dumIndex := 1;
        SELF.DrawLine(i-1, i + howMany - 2, TRUE, FALSE, dumInt, dumIndex);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S TK2Start}
    PROCEDURE TParagraph.DrawLine(startLP, endLP: INTEGER; fDraw: BOOLEAN; fWidth: BOOLEAN;
                                         VAR width: INTEGER; VAR styleIndex: INTEGER);
    {If fDraw = TRUE, draws a line of characters from startLP to endLP; does not worry about word wrap.
     If fWidth = TRUE, returns width of characters.  Also accepts an initial styleIndex (index into
     run array) to make typestyle scanning faster. Returns styleIndex of run of last character drawn.}

    {IDEAS TO MAKE THIS FASTER:
        special check to see if there are no typestyle changes in this para?

    }


    VAR startPP:        INTEGER;
        endPP:	        INTEGER;
        styleChange:    TStyleChange;
        prevChange:     TStyleChange;
        tStyles:        TArray;
        drawCount:      INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            writeln('>> DrawLine: startLP=', startLP:1, '  endLP=', endLP:1,
                    '  styleIndex=', styleIndex:1);
            WriteLn('>> DrawLine: fDraw=', fDraw, '  holeStart =', SELF.holeStart:1,
                    '  holeSize =', SELF.holeSize:1);
            END;
        {$ENDC}
        width := 0;

        tStyles := SELF.typeStyles;
        IF (styleIndex < 1) OR (styleIndex > tStyles.size) THEN
            styleIndex := 1;

        tStyles.GetAt(styleIndex, @styleChange);
        prevChange := styleChange;
        WHILE styleChange.lp < startLP DO
            BEGIN
            prevChange := styleChange;
            styleIndex := styleIndex + 1;
            tStyles.GetAt(styleIndex, @styleChange);
            END;

        PicTextBegin(aLeft);
        SELF.SetTypeStyle(prevChange.newStyle);

        {$IFC fParaTrace}
        IF fParaTrace THEN
            writeln('>> DrawLine: starting to Draw');
        {$ENDC}
        WHILE startLP <= endLP DO
            BEGIN
            drawCount := MIN(styleChange.lp, endLP+1) - startLP;
            IF fWidth THEN
                width := width + TString.Width(startLP+1, drawCount);
            IF fDraw THEN
                BEGIN
                {$IFC fParaTrace}
                IF fParaTrace THEN
                    writeln('>> DrawLine: About to call DrawText; startLP,drawCount=', startLP:1,
                                    ',', drawCount:1);
                {$ENDC}
                TString.Draw(startLP+1, drawCount);
                END;
            startLP := startLP + drawCount;
            IF startLP = styleChange.lp THEN
                BEGIN
                {$IFC fParaTrace}
                IF fParaTrace THEN
                    writeln('>> DrawLine: found a typestyle change at LP=', startLP:1);
                {$ENDC}
                SELF.SetTypeStyle(styleChange.newStyle);
                styleIndex := styleIndex+1;
                tStyles.GetAt(styleIndex, @styleChange)
                END;
            END;

        PicTextEnd;
        styleIndex := styleIndex-1; {return styleIndex of current typeStyle run}
        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            Writeln('>> DrawLine: Finished, width=', width:1);
            WriteLn;
            END;
        {$ENDC}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TParagraph.FindWordBounds(orig: INTEGER; VAR first, last: INTEGER);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        first := orig;
        last := orig;
        IF SELF.Qualifies(orig) THEN
            BEGIN
            WHILE SELF.Qualifies(first - 1) DO first := first - 1;
            WHILE SELF.Qualifies(last + 1) DO last := last + 1;
            END;
        IF last < SELF.size THEN
            last := last+1; {always selects at least one character, except at end of para}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    FUNCTION TParagraph.FixLP(LP: INTEGER): INTEGER;
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF LP < 0 THEN
            FixLP := 0
        ELSE IF LP >= SELF.size THEN
            FixLP := SELF.size
        ELSE
            FixLP := LP;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParagraph.NewStyle(startLP, endLP: INTEGER; newTypeStyle: TTypeStyle);
    VAR styleOfStartLP: TTypeStyle;
        PROCEDURE ChgStyle(VAR typeStyle: TTypeStyle);
        BEGIN
            typeStyle := newTypeStyle;
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.ChangeStyle(startLP, endLP, ChgStyle, styleOfStartLP);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    FUNCTION TParagraph.Qualifies(pos: INTEGER): BOOLEAN;
    VAR i,j:    INTEGER;
        left, this, right:  CHAR;

        FUNCTION CharClass(ch: CHAR): CHAR;
        VAR c:  INTEGER;
        BEGIN
            c := ORD(ch);
            IF c IN [65..90, 97..122, 128..159, 167, 174..175,
                     187..188, 190..191, 202] THEN ch := 'A'
            ELSE IF (48 <= c) AND (c <= 57) THEN ch := '9'
            ELSE IF (c = 162) OR (c = 163) OR (c = 180) THEN ch := '$';
            CharClass := ch;
        END;

        FUNCTION CharAt(i: INTEGER): CHAR;
        BEGIN
            IF i < 0 THEN
                CharAt := ' '
            ELSE IF i >= SELF.size THEN
                CharAt := ' '
            ELSE
                CharAt := SELF.At(i+1);
        END;

    BEGIN {Qualifies}
        {$IFC fTrace}BP(9);{$ENDC}
        left := CharClass(CharAt(pos-1));
        this := CharClass(CharAt(pos));
        right := CharClass(CharAt(pos+1));
        {$IFC fParaTrace}
        IF fParaTrace THEN
            WriteLn('IN QUALIFIES: left, this, right = (', left:1, this:1, right:1, ')  => [',
                        ORD(left):1, ',', ORD(this):1, ',', ORD(right):1, ']');
        {$ENDC}

        FOR i := 1 TO (LENGTH(wordDelimiters) + 1) DIV 4 DO
            BEGIN
            j := 4*i-2; { FOR j := 2 TO LENGTH(wordDelimiters) STEP 4 DO }
            IF ((wordDelimiters[j-1]=left) OR (wordDelimiters[j-1] = 'x')) AND
                (wordDelimiters[j] = this) AND
               ((wordDelimiters[j+1]=right) OR (wordDelimiters[j+1]='x')) THEN
                    BEGIN
                    Qualifies := TRUE;
                    {$IFC fTrace}EP;{$ENDC}
                    EXIT(Qualifies);
                    END;
            END;
        Qualifies := FALSE;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TParagraph.ReplPara(fPos, numChars: INTEGER;
                                       otherPara: TParagraph; otherFPos, otherNumChars: INTEGER);
    VAR styles:	        TArray;
        otherStyles:    TArray;
        styleIndex:     INTEGER;
        otherIndex:     INTEGER;
        styleChange:    TStyleChange;
        otherChange:    TStyleChange;
        prevStyle:      TTypeStyle;
        diff:	        INTEGER;
        endLP:	        INTEGER;
        nextLP:	        INTEGER;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        otherPara.StopEdit;
        SELF.ReplTString(fPos, numChars, otherPara, otherFPos, otherNumChars);
        IF otherNumChars > 0 THEN
            BEGIN

            {COPY THE TYPESTYLE RUN INFO TO SELF.typeStyles}
            otherStyles := otherPara.typeStyles;
            styles := SELF.typeStyles;
            {Find out what run we're in in SELF}
            styleIndex := 1;
            REPEAT
                styleIndex := styleIndex+1;
                styles.GetAt(styleIndex, @styleChange);
            UNTIL fPos <= styleChange.lp;
            nextLP := styleChange.lp;
            IF fPos < nextLP THEN
                styles.GetAt(styleIndex-1, @styleChange); {back up one to get current run}

            {Find the first run in otherPara}
            otherIndex := 1;
            REPEAT
                otherIndex := otherIndex+1;
                otherStyles.GetAt(otherIndex, @otherChange);
            UNTIL otherFPos < otherChange.lp;
            otherStyles.GetAt(otherIndex-1, @otherChange);

            diff := fPos - otherFPos;
            endLP := otherfPos + otherNumChars;
            prevStyle := styleChange.newStyle;
            {Insert the new run info but avoid consecutive run descriptors of the same info}
            otherChange.lp := otherFPos;
            WHILE otherChange.lp <= endLP DO
                BEGIN
                IF TFakeTStyle(otherChange.newStyle) <> TFakeTStyle(prevStyle) THEN
                    BEGIN
                    otherChange.lp := otherChange.lp + diff;
                    styles.InsAt(styleIndex, @otherChange);
                    styleIndex := styleIndex + 1;
                    prevStyle := otherChange.newStyle;
                    END;
                otherStyles.GetAt(otherIndex, @otherChange);
                otherIndex := otherIndex + 1;
                END;

            {Insert descriptor of original run after the inserted info, unless we were on a run boundary
              or the last run has the same font and faces as the original run}
            IF fPos < nextLP THEN
                IF TFakeTStyle(styleChange.newStyle) <> TFakeTStyle(prevStyle) THEN
                    BEGIN
                    styleChange.lp := fPos+otherNumChars;
                    styles.InsAt(styleIndex, @styleChange);
                    END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParagraph.ReplPString(fPos, numChars: INTEGER; pStr: TPString);
        VAR otherNumChars: INTEGER;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}

        { make fPos lie within [0..# chars in paragraph] }
        fPos := SELF.fixLP(fPos);

        IF pStr = NIL THEN
            otherNumChars := 0
        ELSE
            otherNumChars := Length(pStr^);

        SELF.StartEdit(otherNumChars);
        SELF.DelManyAt(fPos + 1, numChars);
        (*
        SELF.UpdateRuns(fPos,-numChars);
        *)
        IF pStr <> NIL THEN
            SELF.InsPStrAt(fPos + 1, pStr);
        SELF.StopEdit;

        SELF.UpdateRuns(fPos, numChars, otherNumChars);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParagraph.ReplTString(fPos, numChars: INTEGER;
                       otherString: TString; otherFPos, otherNumChars: INTEGER);
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}

        { make fPos lie within [0..# chars in paragraph] }
        fPos := SELF.fixLP(fPos);

        SELF.StartEdit(otherNumChars);

        IF numChars > 0 THEN
            SELF.DelManyAt(fPos + 1, numChars);

        IF (otherString <> NIL) AND (otherNumChars > 0) THEN
            SELF.InsManyAt(fPos + 1, otherString, otherFPos + 1, otherNumChars);
        SELF.StopEdit;

        SELF.UpdateRuns(fPos, numChars, otherNumChars);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S TK2Start}
    PROCEDURE TParagraph.SetTypeStyle(tStyle: TTypeStyle);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SetQDTypeStyle(tStyle);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParagraph.StyleAt(lp: INTEGER; VAR typeStyle: TTypeStyle);
    VAR styleChange:    TStyleChange;
        styles:	        TArray;
        styleIndex:     INTEGER;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        styles := SELF.typeStyles;
        styleIndex := 1;
        styles.GetAt(1, @styleChange);
        WHILE styleChange.lp <= lp DO
            BEGIN
            typeStyle := styleChange.newStyle;
            styleIndex := styleIndex+1;
            styles.GetAt(styleIndex, @styleChange);
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParagraph.UpdateRuns(atLP: INTEGER; replacedChars: INTEGER; insertedChars: INTEGER);
    VAR tStyles:        TArray;
        i:	        INTEGER;
        aChange:        TStyleChange;
        tempChange:     TStyleChange;
        prevStyle:      TTypeStyle;
        lastDeleted:    INTEGER;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        tStyles := SELF.typeStyles;
        lastDeleted := -1;
        i := 1;
        WHILE i <= tStyles.size DO
            BEGIN
            tStyles.GetAt(i, @aChange);
            IF atLP <= aChange.lp THEN
                BEGIN
                IF i < tStyles.size THEN
                    aChange.lp := aChange.lp - replacedChars;

                {if we deleted some chars, we must delete associated run info}
                IF aChange.lp <= atLP THEN
                    BEGIN
                    {save type style since we may have deleted only part of this run}
                    tempChange := aChange;

                    {assume whole run deleted, reinsert later if not}
                    tStyles.DelAt(i);
                    lastDeleted := i;
                    i := i-1;
                    END
                ELSE
                    BEGIN
                    IF i = lastDeleted THEN
                        {put back run info for last run deleted if part of it still remains and is not
                         the same as the run before the changes}
                        IF (aChange.lp <> atLP) AND
                            (TFakeTStyle(tempChange.newStyle) <> TFakeTStyle(prevStyle)) THEN
                            BEGIN
                            tempChange.lp := atLP + insertedChars;
                            tStyles.InsAt(i, @tempChange);
                            i := i+1;
                            END;
                    IF i < tStyles.size THEN
                        BEGIN
                        aChange.lp := aChange.lp + insertedChars;
                        tStyles.PutAt(i, @aChange);
                        END;
                    END;
                END
            ELSE
                prevStyle := aChange.newStyle;
            i := i+1;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S TK2Start}
    FUNCTION TParagraph.Width(i: LONGINT; howMany: INTEGER): INTEGER;
    VAR theWidth:   INTEGER;
        dumIndex:   INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        dumIndex := 1;
        SELF.DrawLine(i-1, i + howMany - 2, FALSE, TRUE, theWidth, dumIndex);
        Width := theWidth;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
BEGIN
    fParaTrace := FALSE;
END;

METHODS OF TEditPara;

{$S SgTxtCld}
    FUNCTION TEditPara.CREATE(object: TObject; heap: THeap; initialSize: INTEGER;
                                            itsFormat: TParaFormat): TEditPara;
    VAR imgList:    TList;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF object = NIL THEN
            object := NewDynObject(heap, THISCLASS, initialSize);
        SELF := TEditPara(TParagraph.CREATE(object, heap, initialSize, itsFormat.dfltTStyle));
        imgList := TList.CREATE(NIL, heap, 0);
        WITH SELF DO
            BEGIN
            bsCount := 0;
            nestLevel := 0;
            format := itsFormat;
            beingFiltered := FALSE;
            (*
            numImages := 0;
            maxImages := 1;
            images[1] := NIL;
            *)
            images := imgList;
            END;
        itsFormat.ChangeRefCountBy(1);
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
    PROCEDURE TEditPara.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.format.ChangeRefCountBy(-1);
        SELF.images.FreeObject; {Free the list, but not its members}
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
{$IFC fParaTrace}
    PROCEDURE TEditPara.Fields(PROCEDURE Field(nameAndType: S255));
    VAR str:    STR255;
    BEGIN
        SUPERSELF.Fields(Field);
        Field('bsCount: INTEGER');
        Field('nestLevel: INTEGER');
        Field('format: TParaFormat');
        Field('beingFiltered: BOOLEAN');
        (*
        Field('maxImages: INTEGER');
        Field('numImages: INTEGER');
        IntToStr(SELF.numImages, @str);
        Field(CONCAT('images: ARRAY[1..', CONCAT(str, '] OF TParaImage')));
        *)
        Field('images: TList');
        Field('');
    END;
{$ENDC}

{$S SgTxtHot}
    PROCEDURE TEditPara.BeginInsertion(atLP: INTEGER; size:INTEGER);
    {Changes the text buffer so that the empty space is located at position atLP;
     expands the buffer (if necessary) so that there is at least size empty characters.
     (size = 0 means about to backspace; this does nothing if the paragraph is already
        setup to backspace at atLP.)
    }
    BEGIN
    {$IFC fTrace} BP(6); {$ENDC}
    IF (atLP <> SELF.holeStart) OR (size <> 0) THEN {nothing to do--must be backspacing}
        BEGIN
        SELF.EditAt(atLP + 1, size);
        SELF.bsCount := 0;
        END;
    {$IFC fTrace} EP; {$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TEditPara.DelImage(delImage: TParaImage; fFree: BOOLEAN);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.images.DelObject(delImage, fFree);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    {Selectively delete paraImages from list based on Function Parameter}
    PROCEDURE TEditPara.DelImgIF(FUNCTION ShouldDelete(paraImage: TParaImage): BOOLEAN);
    VAR s:	    TListScanner;
        paraImage:  TParaImage;
        (*
        i:	    INTEGER;
        numDeleted: INTEGER;
        *)
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        s := SELF.images.Scanner;
        WHILE s.Scan(paraImage) DO
            IF ShouldDelete(paraImage) THEN
                s.Delete(FALSE);
        (*
        numDeleted := 0;
        WITH SELF DO
            BEGIN
            i := 1;
            WHILE i <= numImages DO
                BEGIN
                {$R-} {$H-}
                IF ShouldDelete(images[i]) THEN
                    numDeleted := numDeleted+1
                ELSE IF numDeleted > 0 THEN
                    images[i-numDeleted] := images[i];
                {$IFC fRngText}{$R+}{$ENDC} {$H+}
                i := i+1;
                END;
            IF numDeleted > 0 THEN
                BEGIN
                FOR i := (numImages-numDeleted+1) TO numImages DO
                    {$R-}
                    images[numImages] := NIL;
                    {$IFC fRngText}{$R+}{$ENDC}
                numImages := numImages-numDeleted;
                END;
            END;
        *)
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TEditPara.EachImage(PROCEDURE ImageProc(paraImage: TParaImage));
    (*
    VAR i:  INTEGER;
    *)
        PROCEDURE DoIt(object: TObject);
        BEGIN
            ImageProc(TParaImage(object));
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.images.Each(DoIt);
        (*
        FOR i := 1 TO SELF.numImages DO
     {$R-}  ImageProc(SELF.images[i]);  {$IFC fRngText}{$R+}{$ENDC}
        *)
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TEditPara.EndInsertion;
        {After calling this:
            holeStart = emptyPos = # chars in paragraph
        }
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        SELF.StopEdit;
        SELF.bsCount := 0;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    FUNCTION TEditPara.GrowSize: INTEGER;
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        GrowSize := 200;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TEditPara.InsertOneChar(ch: CHAR; atLP: INTEGER);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.BeginInsertion(atLP, 1);	   {UNDO}
        { now we have SELF.holeStart = atLP }
        SELF.PutAt(atLP+1, ch);
        SELF.UpdateRuns(atLP, 0, 1);
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
    PROCEDURE TEditPara.InsImage(paraImage: TParaImage);
    (*
    VAR i:      INTEGER;
        found:  BOOLEAN;
    *)
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.images.InsLast(paraImage);
        (*
        IF SELF.numImages = SELF.maxImages THEN
            BEGIN
            SELF.ResizeCollection(SELF.size + SELF.holeSize + 4);
            SELF.ShiftCollection(0, 4, SELF.size + SELF.holeSize);
            WITH SELF DO
                BEGIN
                dynStart := dynStart + 4;
                maxImages := maxImages + 1;
                END;
            END;
        WITH SELF DO
            BEGIN
            numImages := numImages + 1;
            {$R-}
            images[numImages] := paraImage;
            {$IFC fRngText}{$R+}{$ENDC}
            END;
        *)
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TEditPara.SetTypeStyle(tStyle: TTypeStyle);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.format.SetTypeStyle(tStyle);
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtIni}
END;


METHODS OF TLineInfo;

{$S SgTxtWrm}
    FUNCTION TLineInfo.CREATE(object: TObject; heap: THeap): TLineInfo;
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TLineInfo(object);
        WITH SELF DO
            BEGIN
            valid := FALSE;
            startLP := 0;
            lastDrawnLP := 0;
            endLP := 0;
            lineLRect := zeroLRect;
            lineAscent := 0;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
{$IFC fParaTrace}
    PROCEDURE TLineInfo.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('valid: BOOLEAN');
        Field('startLP: INTEGER');
        Field('lastDrawnLP: INTEGER');
        Field('endLP: INTEGER');
        Field('lineLRect: LRect');
        Field('lineAscent: INTEGER');
        Field('');
    END;
{$ENDC}

{$S SgTxtHot}
    FUNCTION TLineInfo.LeftCoord(proposedLeftPixel: LONGINT): LONGINT;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        {Default is to not change the parameter; TLineInfo subclassers may choose to do otherwise}
        LeftCoord := proposedLeftPixel;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    FUNCTION TLineInfo.RightCoord(proposedRightPixel: LONGINT): LONGINT;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        {Default is to not change the parameter; TLineInfo subclassers may choose to do otherwise}
        RightCoord := proposedRightPixel;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
END;


METHODS OF TParaImage;

{$S SgTxtWrm}
    FUNCTION TParaImage.CREATE(object: TObject; heap: THeap; itsView: TView; itsParagraph: TEditPara;
                               itsLRect: LRect; lineTop: LONGINT; lineLeft: LONGINT): TParaImage;

    VAR aLineList:  TList;
        lineInfo:   TLineInfo;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TParaImage(TImage.CREATE(object, heap, itsLRect, itsView));
        SELF.paragraph := itsParagraph;
        SELF.extentLRect := itsLRect;
        aLineList := TList.CREATE(NIL, heap, 0);
        lineInfo := SELF.DfltLineInfo(lineTop, lineLeft);
        aLineList.InsLast(lineInfo);
        WITH SELF DO
            BEGIN
            height := lineInfo.lineLRect.bottom - lineInfo.lineLRect.top;
            lineList := aLineList;
            tickcount := 0;
            changed := TRUE;
            startLP := 0;
            endLP := 0;
            textImage := NIL;
            wasOffset := FALSE;
            END;

        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TParaImage.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        Free(SELF.lineList);
        (* Since caller of this may be scanning the paragraph's image list we can't delete it from the
           list here lest we screw up the caller's scanner.  So the caller will have to to do this}
        SELF.paragraph.DelImage(SELF, FALSE);
        *)
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
{$IFC fParaTrace}
    PROCEDURE TParaImage.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('paragraph: TEditPara');
        Field('height: INTEGER');
        Field('lineList: TList');
        Field('changed: BOOLEAN');
        Field('tickCount: INTEGER');
        Field('startLP: INTEGER');
        Field('endLP: INTEGER');
        Field('textImage: TTextImage');
        Field('wasOffset: BOOLEAN');
        Field('');
    END;
{$ENDC}

{$S SgTxtHot}
    PROCEDURE TParaImage.AdjustLineLPs(atLP, deltaLP: INTEGER);
        {positive deltaLP implies character(s) were inserted, negative deltaLP implies they were deleted}
        PROCEDURE AdjustLP(obj: TObject);
        BEGIN
            WITH TLineInfo(obj) DO
                BEGIN
                {$H-}
                IF startLP > atLP THEN
                    startLP := Max(atLP, startLP + deltaLP);
                IF lastDrawnLP > atLP THEN
                    lastDrawnLP := Max(atLP, lastDrawnLP + deltaLP);
                IF endLP > atLP THEN
                    endLP := Max(atLP, endLP + deltaLP);
                {$H+}
                END;
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.lineList.Each(AdjustLP);
        WITH SELF DO
            BEGIN
            {$H-}
            IF startLP > atLP THEN
                startLP := Max(atLP, startLP + deltaLP);
            IF endLP >= atLP THEN
                endLP := Max(atLP, endLP + deltaLP);
            {$H+}
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.ComputeLineInfo(curLine: TLineInfo; maxLineLen: INTEGER;
                                                VAR nextLP: INTEGER; VAR lRectNeeded: LRect);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    FUNCTION TParaImage.DfltLineInfo(lineTop: LONGINT; lineLeft: LONGINT): TLineInfo;
    VAR lineInfo:   TLineInfo;
        fInfo:	    FontInfo;
        i:	    INTEGER;
        format:	    TParaFormat;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        lineInfo := TLineInfo.CREATE(NIL, SELF.Heap);

        format := SELF.GetFormat;
        format.SetTypeStyle(format.dfltTStyle);
        GetFontInfo(fInfo);

        i := SELF.paragraph.size;
        WITH lineInfo, fInfo, format DO
            BEGIN
            lastDrawnLP := i;
            endLP := i;
            lineAscent := ascent;
            {$H-}
            SetLRect(lineLRect, lineLeft, lineTop, lineLeft,
                        spaceAbovePara + lineTop + ascent + descent + leading + spaceBelowPara);
            OffsetLRect(lineLRect, SELF.extentLRect.left + firstIndent, SELF.extentLRect.top);
            {$H+}
            END;
        DfltLineInfo := lineInfo;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.DrawLine(startLP: INTEGER; fDraw: BOOLEAN; stopWidth, wrapWidth: INTEGER;
                                     VAR lineWidth, lastToDraw, endLP: INTEGER);
    {Figures out what characters to draw based on a variety of input constraints.
            Returns:
                lineWidth:  the width of the line calculated (including trailing spaces) [??]
                lastToDraw: the lp of the last non blank character in the line
                endLP:	    the lp of the last character in the line (may be a blank)	        }

    {NOTE: the wrapWidth parameter may eventually be dropped and instead calculated from SELF.extentLRect
            and certain format fields}

    LABEL   1;
    VAR c:	        CHAR;
        startPP:        INTEGER;
        curIndex:       INTEGER;       { PP of last character looked at }
        styleChange:    TStyleChange;
        prevChange:     TStyleChange;
        styles:	        TArray;
        styleIndex:     INTEGER;
        firstStyleIndex:INTEGER;
        cWidth:	        INTEGER;
        maxPP:	        INTEGER;
        endPP:	        INTEGER;
        breakIndex:     INTEGER;
        breakCount:     INTEGER;
        breakLen:       INTEGER;
        paragraph:      TEditPara;
        format:	        TParaFormat;
        drawStart:      INTEGER;
        drawCount:      INTEGER;
        dummy:	        INTEGER;
        maxLP:	        INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {$IFC fParaTrace}
        IF fParaTrace THEN
            writeln('** DrawLine: startLP=', startLP:1, ', maxLP=',maxLP:1, ', fDraw=', fDraw,
                    ', stopWidth=',stopWidth:1, ', wrapWidth=',wrapWidth:1);
        {$ENDC}

        maxLP := SELF.paragraph.size-1;
        IF maxLP < startLP THEN
            BEGIN
            lineWidth := 0;
            lastToDraw := maxLP;
            endLP := maxLP;
            END
        ELSE
            BEGIN
            paragraph := SELF.paragraph;
            format := SELF.GetFormat;

            breakIndex := 0;
            lastToDraw := 0;
            lineWidth := 0;
            cWidth := 0;
            curIndex := paragraph.FixLP(startLP);
            styles := paragraph.typeStyles;
            styles.GetAt(1, @styleChange);
            styleIndex := 1;
            REPEAT
                prevChange := styleChange;
                styleIndex := styleIndex + 1;
                styles.GetAt(styleIndex, @styleChange);
            UNTIL curIndex < styleChange.lp;

            format.SetTypeStyle(prevChange.newStyle);
            firstStyleIndex := styleIndex-1;

            startPP := curIndex;
            maxPP := MIN(paragraph.size, paragraph.FixLP(maxLP));

            {$IFC fParaTrace}
            IF fParaTrace THEN
                writeln('** DrawLine: About enter loop, maxPP =',maxPP:1,'  holeStart=',paragraph.holeStart:1,
                        '  holeSize=',paragraph.holeSize:1);
            {$ENDC}
            WHILE curIndex <= maxPP DO
                BEGIN
                IF curIndex = styleChange.lp THEN
                    BEGIN
                    format.SetTypeStyle(styleChange.newStyle);
                    styleIndex := styleIndex+1;
                    styles.GetAt(styleIndex, @styleChange)
                    END;

                c := paragraph.At(curIndex+1);

                cWidth := CharWidth(c);
                {$IFC fParaTrace AND FALSE}
                IF fParaTrace THEN
                    writeln('curIndex=', curIndex:1, ', char=', c, ', cWidth=',
                                cWidth:1, ', lineWidth=',lineWidth:1);
                {$ENDC}


                {Drop out of loop if lineWidth > stopWidth unless
                    we're at end of line and have trailing spaces}
                IF (lineWidth + cWidth > stopWidth) THEN
                    IF format.wordWrap AND (stopWidth = wrapWidth) THEN
                        IF (c <> ' ') THEN
                            GOTO 1
                        ELSE
                    ELSE
                        GOTO 1;

                IF format.wordWrap AND (c = ' ') THEN
                    BEGIN
                    IF (breakIndex + 1) < curIndex THEN	 {so we don't draw trailing blanks}
                        lastToDraw := curIndex-1;
                    breakIndex := curIndex;
                    breakLen := lineWidth;
                    END;

                lineWidth := lineWidth + cWidth;
                curIndex := curIndex + 1;
                cWidth := 0;
                END;
    1:
            curIndex := curIndex - 1;
            IF format.wordWrap AND (lineWidth + cWidth > wrapWidth) AND (breakIndex > 0) THEN
                { PRIMITIVE WORD WRAP! }
                BEGIN
                lineWidth := breakLen;
                curIndex := breakIndex;
                END
            ELSE
                lastToDraw := curIndex;

            {$IFC fParaTrace}
            IF fParaTrace THEN
                writeln('** DrawLine: About to figure endLP, curIndex =',curIndex:1);
            {$ENDC}

            endLP := curIndex;

            {$IFC fParaTrace}
            IF fParaTrace THEN
                writeln('** DrawLine: endLP figured =',endLP:1);
            {$ENDC}

            IF (lastToDraw >= 0) AND fDraw THEN
                SELF.FastDrawLine(paragraph.fixLP(startLP), lastToDraw, TRUE,
                                                        FALSE, dummy, firstStyleIndex);
            END;

        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            writeln('** DrawLine done: endLP=',endLP:1,', lineWidth=',lineWidth:1);
            writeln('** DrawLine done: final lastToDraw=',lastToDraw:1);
            WriteLn;
            END;
        {$ENDC}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.DrawParaImage(limitLRect: LRect; startLP: INTEGER; drawAction: TDrawAction;
                                            invalBits: BOOLEAN; VAR drawnLRect: LRect);
    {Note: DrawParaImage now assumes that the paragraph was changed}
    LABEL 1;
    VAR paragraph:      TEditPara;
        fInfo:	        FontInfo;
        lineInfo:       TLineInfo;
        firstLineInfo:  TLineInfo;
        lineList:       TList;
        lineSpacing:    INTEGER;
        curBase:        LONGINT;
        leftMargin:     LONGINT;
        curLP:	        INTEGER;
        endLP:	        INTEGER;
        numChars:       INTEGER;
        lineLen:        INTEGER;
        maxLineLen:     INTEGER;
        lineIndex:      INTEGER;
        pixel:	        LONGINT;
        lastDrawnLP:    INTEGER;
        testLPoint:     LPoint;
        format:	        TParaFormat;
        firstFudge:     INTEGER;
        startOfNewPara: BOOLEAN;
        anLRect:        LRect;
        sLine:	        TListScanner;
        genRest:        BOOLEAN;
        genBefore:      BOOLEAN;
        oldEndLP:       INTEGER;
        prevLineInfo:   TLineInfo;
        prevLen:        INTEGER;
        prevPImage:     TParaImage;
        prevTxtImage:   TTextImage;
        origStart:      INTEGER;
        dummy:	        INTEGER;
        styleIndex:     INTEGER;
        r:	        LRect;
        {$IFC fParaTrace}
        str:	        STR255;
        {$ENDC}
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            WITH limitLRect DO
                WriteLn('## Entering DrawParaImage: limitLRect=[(',left:1,',',top:1,'),(',
                                                                    right:1,',', bottom:1,')]');
            LIntToHex(ORD(SELF), @str);
            WriteLn(    '       SELF = ', str,' startLP=',startLP:1,'  drawAction=',ORD(drawAction));
            END;
        {$ENDC}

        drawnLRect := limitLRect;
        endLP := startLP;
        paragraph := SELF.paragraph;
        format := SELF.GetFormat;
        IF drawAction = actionDraw THEN WITH SELF DO
            tickCount := (tickCount+1) MOD MAXINT;

        PicGrpBegin;
        PenNormal;

        genRest := FALSE;
        genBefore := FALSE;
        curLP := startLP;
        numChars := paragraph.size;
        SELF.startLP := curLP;

        format.SetTypeStyle(format.dfltTStyle);
        GetFontInfo(fInfo);
        WITH fInfo DO
            lineSpacing := ascent + descent + leading + format.lineSpacing;

        lineList := SELF.lineList;
        curBase := limitLRect.top;
        prevLineInfo := NIL;
        IF lineList.Size > 0 THEN
            BEGIN
            sLine := lineList.Scanner;
            {If existing lineInfo's start after startLP then we need to generate preceeding lineInfo's}
            IF TLineInfo(lineList.First).startLP > startLP THEN
                BEGIN
                origStart := TLineInfo(lineList.First).startLP;
                genBefore := TRUE;
                END
            ELSE WHILE sLine.Scan(lineInfo) DO
                BEGIN
                {delete lineinfo's that start before the startLP parameter}
                IF lineInfo.endLP < startLP THEN
                    sLine.Delete(TRUE)
                ELSE
                    BEGIN
                    IF lineInfo.valid THEN
                        BEGIN
                        prevLineInfo := lineInfo;
                        curBase := lineInfo.lineLRect.bottom;
                        END
                    ELSE
                        GOTO 1;
                    END;
                END;
            END
        ELSE
            lineInfo := NIL;

        1:
        IF NOT genBefore THEN
            IF lineInfo = NIL THEN
                BEGIN
                genRest := TRUE;
                curLP := Max(startLP, SELF.endLP);
                END
            ELSE
                curLP := Max(startLP, lineInfo.startLP);

        startOfNewPara := curLP = 0;

        curBase := curBase + fInfo.ascent;
        IF startOfNewPara THEN
            BEGIN
            curBase := curBase + format.spaceAbovePara;
            leftMargin := limitLRect.left + format.firstIndent;
            { The first line maxLineLen might be different (due to firstIndent)}
            firstFudge := format.firstIndent - format.leftIndent;
            END
        ELSE
            BEGIN
            leftMargin := limitLRect.left + format.leftIndent;
            firstFudge := 0;
            END;

        limitLRect.left := limitLRect.left + format.leftIndent;
        limitLRect.right := limitLRect.right - format.rightIndent;

        maxLineLen := lengthLRect(limitLRect, h) - firstFudge;  {if firstIndent is to left of
                                                                 leftIndent, fudge will be negative}

        SetLPt(testLPoint, limitLRect.left, curBase + fInfo.descent);

        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            WriteLn('## DrawParaImage: Entering DrawLine loop -- leftMargin=',leftMargin:1,
                            '  curBase=',curBase:1);
            END;
        {$ENDC}

        {don't bother going into the loop if we can't fit the first line or we've run out
         of characters already (eg: empty para)}
        IF (NOT LPtInLRect(testLPoint, limitLRect)) OR (curLP >= numChars) THEN
            BEGIN
            SELF.extentLRect := drawnLRect;
            IF NOT genRest THEN
                BEGIN
                r := lineInfo.lineLRect;
                r.left := lineInfo.LeftCoord(SELF.textImage.extentLRect.left-1);
                r.right := lineInfo.RightCoord(SELF.textImage.extentLRect.right+1);
                lineInfo := SELF.DfltLineInfo(0,0);
                r.bottom := lineInfo.lineLRect.bottom;
                sLine.Replace(lineInfo, TRUE);
                genRest := NOT sLine.Scan(lineInfo);
                END;
            {These two assignments distinguish (for the calling routine) whether an empty
             paragraph did or did not fit in the limitLRect.  Assuming curLP = 0, SELF.endLP
             will be 0 for a paragraph that did fit and -1 for one that did not fit.  The calling
             routine checks this value against paragraph.size to see if the paragraph fit}
            IF LPtInLRect(testLPoint, limitLRect) THEN
                BEGIN
                SELF.endLP := curLP;
                {Erase the old line}
                IF drawAction = actionDraw THEN
                    FillLRect(r, lPatWhite)
                ELSE IF drawAction = actionInval THEN
                    thePad.InvalLRect(r);
                END
            ELSE
                SELF.endLP := curLP-1;
            {$IFC fParaTrace}
            IF fParaTrace THEN
                BEGIN
                WriteLn('## DrawParaImage: Empty para or cannot fit; endLP set to ',SELF.endLP:1);
                END;
            {$ENDC}
            END
        {Otherwise, set up lineLRect and call DrawLine while there are still characters to display
         and we still fit in limitLRect}
        ELSE
            BEGIN
            {Layout line previous to first invalid line to see if characters from the
             invalid line can wrap back.  First, however, we must check for special case
             of the previous line being in another textImage.}
            IF NOT startOfNewPara AND (prevLineInfo = NIL) THEN
                BEGIN
                prevTxtImage := SELF.textImage.prevTxtImg;
                IF prevTxtImage <> NIL THEN
                    BEGIN
                    prevPImage := TParaImage(prevTxtImage.imageList.Last);
                    prevLineInfo := TLineInfo(prevPImage.lineList.Last);
                    prevLen := LengthLRect(prevPImage.extentLRect, h) - format.leftIndent
                                                                      - format.rightIndent;
                    END;
                END
            ELSE
                BEGIN
                prevLen := maxLineLen;
                prevPImage := SELF;
                END;

            IF prevLineInfo <> NIL THEN
                BEGIN
                oldEndLP := prevLineInfo.endLP;
                IF prevLineInfo.startLP = 0 THEN
                    prevLen := prevLen - (format.firstIndent - format.leftIndent);

                prevPImage.DrawLine(prevLineInfo.startLP, FALSE, prevLen, prevLen,
                                            lineLen, lastDrawnLP, endLP);

                IF endLP <> oldEndLP THEN
                    BEGIN
                    SELF.textImage.useFirstPixel := FALSE;
                    r := prevLineInfo.lineLRect;
                    r.left := prevLineInfo.LeftCoord(prevPImage.textImage.extentLRect.left-1);
                    r.right := prevLineInfo.RightCoord(prevPImage.textImage.extentLRect.right+1);
                    IF drawAction = actionDraw THEN
                        BEGIN
                        FillLRect(r, lPatWhite);
                        styleIndex := 1;
                        MoveToL(prevLineInfo.lineLRect.left,
                                prevLineInfo.lineLRect.top+prevLineInfo.lineAscent);
                        SELF.FastDrawLine(prevLineInfo.startLP, lastDrawnLP, TRUE,
                                                    FALSE, dummy, styleIndex);
                        END
                    ELSE IF drawAction = actionInval THEN
                        thePad.InvalLRect(r);

                    WITH prevLineInfo.lineLRect DO
                        BEGIN
                        prevLineInfo.valid := invalBits;
                        right := left + lineLen;
                        prevLineInfo.lastDrawnLP := lastDrawnLP;
                        prevLineInfo.endLP := endLP;
                        curLP := endLP + 1;
                        prevPImage.endLP := curLP;
                        IF curLP >= numChars THEN
                            IF (bottom + format.spaceBelowPara) <= limitLRect.bottom THEN
                                BEGIN
                                r.top := bottom;
                                bottom := bottom + format.spaceBelowPara;
                                r.bottom := bottom;
                                {$H-}
                                IF drawAction = actionDraw THEN
                                    FillLRect(r, lPatWhite)
                                ELSE IF drawAction = actionInval THEN
                                    thePad.InvalLRect(r);
                                {$H+}
                                END;
                        END;
                    END;
                END;
            WITH fInfo DO
                SetLRect(anLRect, leftMargin, curBase - ascent,
                              leftMargin + maxlineLen, curBase + descent + leading);
            leftMargin := limitLRect.left;
            {Setup GrafPort for first line (after prev line)}
            IF drawAction = actionDraw THEN
                MoveToL(leftMargin + firstFudge, curBase);

            WHILE (curLP < numChars) AND (LPtInLRect(testLPoint, limitLRect)) DO
                BEGIN
                IF genRest OR genBefore THEN
                    lineInfo := TLineInfo.CREATE(NIL, paragraph.heap);

                IF NOT lineInfo.valid THEN
                    BEGIN
                    WITH fInfo, lineInfo DO
                        BEGIN
                        startLP := curLP;
                        lineAscent := ascent;
                        lineLRect := anLRect;
                        END;

                    r := anLRect;
                    r.left := lineInfo.LeftCoord(SELF.textImage.extentLRect.left-1);
                    IF SELF.textImage.useFirstPixel THEN
                        BEGIN
                        r.left := Max(r.left, SELF.textImage.firstLinePixel);
                        SELF.textImage.useFirstPixel := FALSE;
                        END;
                    r.right := lineInfo.RightCoord(SELF.textImage.extentLRect.right+1);

                    IF drawAction = actionDraw THEN
                        FillLRect(r, lPatWhite)
                    ELSE IF drawAction = actionInval THEN
                        thePad.InvalLRect(r);

                    oldEndLP := lineInfo.endLP;

                    SELF.DrawLine(curLP, drawAction = actionDraw, maxLineLen, maxLineLen,
                                            lineLen, lastDrawnLP, endLP);

                    {$IFC fParaTrace}
                    IF (curLP > endLP) AND (curLP < numChars)THEN
                        BEGIN
                        ABCbreak('loop in DrawParaImage; curLP=',curLP);
                        endLP := curLP + 1;
                        END;
                    {$ENDC}

                    {finish setting up new lineInfo}
                    WITH fInfo, lineInfo.lineLRect DO
                        BEGIN
                        lineInfo.lastDrawnLP := lastDrawnLP;
                        lineInfo.endLP := endLP;
                        right := left + lineLen;

                        {if this is last line in paragraph, add spaceBelowPara, unless that extra amount
                         would put it ouside of the limitLRect}
                        IF (endLP+1) >= numChars THEN
                            IF (bottom + format.spaceBelowPara) <= limitLRect.bottom THEN
                                BEGIN
                                r.top := bottom;
                                bottom := bottom + format.spaceBelowPara;
                                r.bottom := bottom;
                                {$H-}
                                IF drawAction = actionDraw THEN
                                    FillLRect(r, lPatWhite)
                                ELSE IF drawAction = actionInval THEN
                                    thePad.InvalLRect(r);
                                {$H+}
                                END;
                        END;

                    {If the word being typed wrapped down to the next line, we need to erase
                     the piece of the word that was on this line.}
                    IF r.left > lineInfo.lineLRect.right THEN
                        BEGIN
                        r.left := lineInfo.lineLRect.right;
                        IF drawAction = actionDraw THEN
                            FillLRect(r, lPatWhite)
                        ELSE IF drawAction = actionInval THEN
                            thePad.InvalLRect(r);
                        END;

                    IF genRest THEN
                        lineList.InsLast(lineInfo)
                    ELSE IF genBefore THEN
                        sLine.Append(lineInfo); {leaves scanner poised before the original first lineInfo}

                    lineInfo.valid := invalBits;
                    END
                ELSE {lineInfo is valid}
                    BEGIN
                    endLP := lineInfo.endLP;
                    oldEndLP := endLP;
                    END;

                {This field is used by caller in case the entire paragraph didn't fit, so that the
                 caller knows where to start subsequent display}
                SELF.endLP := endLP + 1;
                {Setup for next line}
                curLP := endLP+1;
                curBase := curBase + lineSpacing;
                maxLineLen := maxLineLen + firstFudge;
                firstFudge := 0;
                IF genBefore THEN
                    genBefore :=  origStart > curLP;

                IF NOT (genRest OR genBefore) THEN
                    BEGIN
                    IF sLine.Scan(lineInfo) THEN
                        IF lineInfo.startLP <> curLP THEN
                            lineInfo.valid := FALSE
                        ELSE
                    ELSE
                        genRest := TRUE;
                    END;

                { setup GrafPort and lineRect for next line}
                IF drawAction = actionDraw THEN
                    MoveToL(leftMargin, curBase);
                WITH fInfo DO
                    SetLRect(anLRect,
                            leftMargin, curBase - ascent,
                            leftMargin + maxLineLen, curBase + descent + leading);
                SetLPt(testLPoint, leftMargin, testLPoint.v + lineSpacing);
                END; {WHILE}
            END; {IF}
        {We don't want to delete the lineInfo we just sLine.appended so advance scanner}
        IF genBefore THEN
            sLine.Skip(1);
        IF NOT genRest THEN
            REPEAT
                sLine.Delete(TRUE);
            UNTIL NOT sLine.Scan(lineInfo);

        IF SELF.changed THEN
            SELF.changed := NOT invalBits;

        PicGrpEnd;

        lineInfo := TLineInfo(SELF.lineList.Last);
        firstLineInfo := TLineInfo(SELF.lineList.First);
        SELF.height := lineInfo.lineLRect.bottom - firstLineInfo.lineLRect.top;
        drawnLRect.bottom := lineInfo.lineLRect.bottom;

        SELF.extentLRect := drawnLRect;

        {$IFC fParaTrace}
        IF fParaTrace THEN
            BEGIN
            WITH drawnLRect DO
                WriteLn('## Exiting DrawParaImage: drawnLRect=[(',left:1,',',top:1,'),(',
                                                                    right:1,',', bottom:1,')]',
                                                                    '; height = ',SELF.height:1);
            END;
        {$ENDC}

        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParaImage.Draw;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.RedrawLines(0, MAXINT);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.FastDrawLine(startLP, endLP: INTEGER; fDraw: BOOLEAN; fWidth: BOOLEAN;
                                         VAR width: INTEGER; VAR styleIndex: INTEGER);
    {If fDraw = TRUE, draws a line of characters from startLP to endLP; does not worry about word wrap.
     If fWidth = TRUE, returns width of characters.  Also accepts an initial styleIndex (index into
     run array) to make typestyle scanning faster. Returns styleIndex of run of last character drawn.}

    VAR saveFormat: TParaFormat;
        paragraph:  TEditPara;
        format:	    TParaformat;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        paragraph := SELF.paragraph;
        saveFormat := paragraph.format;
        format := SELF.GetFormat;
        paragraph.format := format;
        paragraph.DrawLine(startLP, endLP, fDraw, fWidth, width, styleIndex);
        paragraph.format := saveFormat;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    {Returns paragraph's paraFormat; can be overriden by application}
    FUNCTION TParaImage.GetFormat: TParaFormat;
    VAR styleIndex:     INTEGER;
    BEGIN
        {$IFC fTrace}BP(7);{$ENDC}
        GetFormat := SELF.paragraph.format;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.InvalLinesWith(startLP, endLP: INTEGER);
    VAR s:	        TListScanner;
        lineInfo:       TLineInfo;
        prevLineInfo:   TLineInfo;
        numChars:       INTEGER;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        SELF.changed := TRUE;
        s := SELF.lineList.Scanner;
        IF s.Scan(prevLineInfo) THEN
            BEGIN
            WHILE s.Scan(lineInfo) DO
                BEGIN
                {If its already invalid, don't muck with it}
                IF prevLineInfo.valid THEN
                    prevLineInfo.valid := (lineInfo.startLP <= startLP) OR (prevLineInfo.startLP > endLP);
                prevLineInfo := lineInfo;
                END;

            {last line}
            IF prevLineInfo.valid THEN
                BEGIN
                numChars := SELF.paragraph.size;
                prevLineInfo.valid := (numChars < startLP) OR (prevLineInfo.startLP > endLP);
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TParaImage.LineWithLPt(pt: LPoint; VAR lineIndex: INTEGER; VAR lineInfo: TLineInfo);
    VAR s:	    TListScanner;
        nxtLnInfo:  TLineInfo;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        {$IFC fParaTrace}
        IF fParaTrace THEN
            writeln('in LineWithLPt, point=(,',pt.h:1,', ',pt.v:1,')');
        {$ENDC}

        s := SELF.lineList.scanner; {&&& maybe could use TList.scannerFrom(index)}
        lineIndex := 1;

        {This has been modified to allow for the possibility of multiple lineRects at the same
         vertical coordinate }
        IF s.Scan(lineInfo) THEN
            WHILE s.scan(nxtLnInfo) DO
                BEGIN
                IF (pt.v < lineInfo.lineLRect.bottom) AND
                    ((pt.h < lineInfo.lineLRect.right) OR (pt.v < nxtLnInfo.lineLRect.top)) THEN
                        s.Done
                ELSE
                    BEGIN
                    lineIndex := lineIndex + 1;
                    lineInfo := nxtLnInfo;
                    END;
                END
        ELSE
            BEGIN
            {$IFC fParaTrace}
            IF fParaTrace THEN
                writeln(chr(7), 'LineWithLPt: no TLineInfo in TParaImage, lineIndex=0');
            {$ENDC}
            lineIndex := 1;
            lineInfo := SELF.DfltLineInfo(0, 0);
            SELF.lineList.InsLast(lineInfo);
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.LocateLP(LP: INTEGER; VAR lineIndex: INTEGER; VAR pixel: LONGINT);
    VAR s:	    TListScanner;
        lstLnInfo:  TLineInfo;
        lineInfo:   TLineInfo;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        IF LP < 0 THEN
            LP := 0;
        s := SELF.lineList.Scanner;
        lineIndex := 0;

        WHILE s.Scan(lineInfo) DO
            BEGIN
            IF LP < lineInfo.startLP THEN
                s.Done
            ELSE
                BEGIN
                lineIndex := lineIndex + 1;
                lstLnInfo := lineInfo;
                END;
            END;

        IF lineIndex=0 THEN
            BEGIN
            {$IFC fParaTrace}
            IF fParaTrace THEN
                writeln(chr(7), 'LocateLP: no TLineInfo in TParaImage, lineIndex=0');
            {$ENDC}
            lineIndex := 1;
            lineInfo := SELF.DfltLineInfo(0, 0);
            pixel := lineInfo.lineLRect.left - 1;    { leave 1 pixel space before character }
            SELF.lineList.InsLast(lineInfo);
            END
        ELSE
            BEGIN
            pixel := lstLnInfo.lineLRect.left + SELF.ParaTextWidth(lstLnInfo.startLP, LP-1) - 1;
                                                                { leave 1 pixel space before character }
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    FUNCTION TParaImage.LpWithLPt(pt: LPoint): INTEGER;
    VAR lineIndex:  INTEGER;
        lineInfo:   TLineInfo;
        endLP:	    INTEGER;
        lineLen:    INTEGER;
        charWid:    INTEGER;
        paragraph:  TEditPara;
        wrapMargin: INTEGER;
        lastLP:	    INTEGER;
        PROCEDURE DrawLine(obj: TObject);  {This routine gets filtered after a type style change}
        BEGIN
            SELF.DrawLine(lineInfo.startLP, FALSE, pt.h-lineInfo.lineLRect.left, wrapMargin,
                                lineLen, lastLP, endLP);
        END;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        SELF.LineWithLPt(pt, lineIndex, lineInfo);
        IF pt.v < lineInfo.lineLRect.top THEN
            pt := lineInfo.lineLRect.topLeft
        ELSE IF pt.v > lineInfo.lineLRect.bottom THEN
            pt := lineInfo.lineLRect.botRight
        ELSE
            LRectHaveLPt(lineInfo.lineLRect, pt);

        paragraph := SELF.paragraph;
        wrapMargin := lengthLRect(SELF.extentLRect, h);

        SELF.FilterAndDo(SELF, DrawLine);

        { endLP is now the LP of the character before the character the cursor was over}
        lineLen := lineLen + lineInfo.lineLRect.left;
        { lineLen is now the x-coord of screen position of endLP (right pixel) }

        endLP := MIN(endLP+1, paragraph.size);
        charWid := SELF.paraTextWidth(endLP, endLP);   { find width of the char under cursor }
        {$IFC fParaTrace}
        IF fParaTrace THEN
            writeln('LpWithLPt: endLP=', endLP:1, '  pt.h=', pt.h:1, '  lineLen=', lineLen:1,
                            '  charWid=', charWid:1);
        {$ENDC}
        IF 2*(pt.h-lineLen) >= charWid THEN  { pt is right of center of char }
            LpWithLPt := paragraph.fixLP(endLP+1)
        ELSE				    { pt is left of center of char }
            LpWithLPt := paragraph.fixLP(endLP);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TParaImage.OffsetBy(deltaLPt: LPoint);
    VAR s:	        TListScanner;
        lineInfo:       TLineInfo;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {&&& should make sure the results falls within view}
        WITH deltaLPt DO
            BEGIN
            {$H-}   OffsetLRect(SELF.extentLRect, h, v);    {$H+}
            s := SELF.lineList.Scanner;
            WHILE s.Scan(lineInfo) DO
                {$H-}   OffsetLRect(lineInfo.lineLRect, h, v);	  {$H+}
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    {Returns width of characters in range startLP, endLP. (NOTE: startLP=endLP for width of one char)}
    FUNCTION TParaImage.ParaTextWidth(startLP, endLP: INTEGER): INTEGER;
    VAR styleIndex:     INTEGER;
        width:	        INTEGER;
        PROCEDURE FastDraw(obj: TObject);  {This routine gets filtered after a type style change}
        BEGIN
            SELF.FastDrawLine(startLP, endLP, FALSE, TRUE, width, styleIndex);
        END;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        styleIndex := 1;
        IF endLP < startLP THEN
            width := 0
        ELSE
            SELF.FilterAndDo(SELF, FastDraw);
        ParaTextWidth := width;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TParaImage.RedrawLines(startLine: INTEGER; endLine: INTEGER);
    VAR s:	        TListScanner;
        i:	        INTEGER;
        lineInfo:       TLineInfo;
        prevLineInfo:   TLineInfo;
        styleIndex:     INTEGER;
        dummy:	        INTEGER;
        PROCEDURE FastDraw(obj: TObject);  {This routine gets filtered after a type style change}
        BEGIN
            SELF.FastDrawLine(lineInfo.startLP, lineInfo.lastDrawnLP, TRUE,
                                                            FALSE, dummy, styleIndex);
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        PicGrpBegin;
        s := SELF.lineList.Scanner;
        i := 0;
        endLine := Min(endLine, SELF.lineList.Size);
        startLine := Max(startLine, 1);
        styleIndex := 1;
        WHILE s.scan(lineInfo) DO
            BEGIN
            i := i+1;

            IF i < startLine THEN {nothing}
            ELSE
                BEGIN
                IF LRectIsVisible(lineInfo.lineLRect) THEN
                    BEGIN
                    {$IFC fParaTrace}
                    IF fParaTrace THEN
                        writeln('## ReDrawLines: About to call FastDraw; i=', i:1);
                    {$ENDC}
                    MoveToL(lineInfo.lineLRect.left, lineInfo.lineLRect.top+lineInfo.lineAscent);
                    SELF.FilterAndDo(SELF, FastDraw);
                    END;
                IF i = endLine THEN
                    s.Done;
                END;
            END;
        PicGrpEnd;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TParaImage.SeesSameAs(image: TImage): BOOLEAN;
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF SELF = image THEN
            SeesSameAs := TRUE
        ELSE IF NOT InClass(image, TParaImage) THEN
            SeesSameAs := FALSE
        ELSE
            SeesSameAs := SELF.paragraph = TParaImage(image).paragraph;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
END;
{$S SgTxtIni}

